package frame.util.generator;

import freemarker.template.Configuration;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 数据库逆向工程
 */
public abstract class Generator {
    //数据库配置属性
    protected String driver;
    protected String url;
    protected String username;
    protected String password;
    protected Connection connection;

    // Java代码配置
    protected Configure configure;
    //步骤
    protected int step;
    //流程开关
    protected boolean runAble;
    //主键字段
    protected Column2Field id;
    //非主键字段列表
    protected List<Column2Field> fieldList;
    // 父类公共字段
    protected List<Column2Field> superFieldList;
    // 父类字段对应的列 用于排除
    protected Set<String> superColumnSet;
    // 查询列 t.xxx
    protected String columnNameSqlList;
    //需要导入的包
    protected Set<String> entityImportSet;
    protected Set<String> mapperImportSet;
    // 模板引擎
    protected Configuration cfg;

    public Generator() {
        configure = new Configure();
        step = 0;
        runAble = true;
        fieldList = new ArrayList<>();
        superFieldList = new ArrayList<>();
        superColumnSet = new HashSet<>();
        entityImportSet = new HashSet<>();
        mapperImportSet = new HashSet<>();
        init();
    }

    public Generator setConfigure(String modelPackage, String modelName) {
        step++;
        if (step != 1) {
        	showError("步骤错误，流程终止！","第一步请执行 setConfigure(String modelPackage, String modelName)", false);
        	return this;
        }
        if (isEmpty(modelPackage)) {
            showError("模块包未指定，流程终止！", "modelPackage 参数错误", false);
            return this;
        }
        if (isEmpty(modelName)) {
            showError("模块名未指定，流程终止！", "modelName 参数错误", false);
            return this;
        }

        String projectDir = System.getProperty("user.dir");
        this.configure.setModel(projectDir, modelPackage, modelName);
        showSuccess("Java配置成功：\n\tprojectDir=" + projectDir + "\n\tmodelPackege=" + modelPackage  + "\n\tmodelName=" + modelName);
        this.configure.show("\t|-\t","\t|- 以上参数配置结果如下：");
        return this;
    }

    public Generator setAuthor(String author) {
    	step++;
        if (step != 2) {
        	showError("步骤错误，流程终止！","第二步请执行 setAuthor(String author)", false);
        	return this;
        }
        if (isEmpty(author)) {
            showError("作者未指定，流程终止！", "author 参数错误", false);
            return this;
        }
        this.configure.setAuthorSince(author, new SimpleDateFormat("yyyy-MM-dd HH:mm").format(new Date()));
        return this;
    }
    public Generator setQuery(Class<?> clazz) {
        step++;
        if (step != 3) {
        	showError("步骤错误，流程终止！","第三步请执行setQuery(Class<?> clazz)", false);
        	return this;
        }
        if (clazz == null) {
            showError("查询对象未指定，流程终止！","clazz参数为空", false);
            return this;
        }
        this.configure.setQuery(clazz);
        showSuccess("查询对象配置成功：\n\tqueryPackage="+configure.getQueryPackage()+"\n\tqueryName="+configure.getQueryName());
        return this;
    }

    public Generator setSuper(Class<?> clazz) {
        step++;
        if (step != 4) {
            showError("步骤错误，流程终止！","第四步请执行setSuper(Class<?> clazz)", false);
            return this;
        }
        if (clazz == null) {
            showSuccess("父类对象未指定，数据表所有字段都将添加到实体类");
            return this;
        }
        Field[] fields = clazz.getDeclaredFields();

        StringBuilder sb = new StringBuilder();
        for (Field field : fields) {
            String filedName = field.getName();
            String columnName = humpToLine(filedName);
            Column2Field c2f = new Column2Field();
            c2f.setFieldName(filedName);
            c2f.setColumnName(columnName);
            superFieldList.add(c2f);
            superColumnSet.add(columnName);
            sb.append(field.getName()).append(",");
        }
        showSuccess("父类对象配置成功：\n\t以下字段不会加入实体类："+sb.toString());
        return this;
    }
    /**
     * 读取数据库表信息</br>
     * 不同数据库解析方法不一致
     */
    public Generator setTable(String tableName) {
        step++;
        if (step != 5) {
        	showError("步骤错误，流程终止！","第五步请执行setTable(String tableName)", false);
        	return this;
        }
        if (isEmpty(tableName)) {
            showError("数据库表名未指定，流程终止！", "tableName 参数错误", false);
            return this;
        }
        this.configure.setTableName(tableName);
        loadProperties();
        openConnection();
        convertTable2Entity();
        closeConnection();
        convertColumnNameSqlList();
        return this;
    }


    // 加载配置
    private void loadProperties() {
        ResourceBundle resource = ResourceBundle.getBundle("application-dev");
        driver = resource.getString("spring.datasource.driver-class-name");
        url = resource.getString("spring.datasource.url");
        username = resource.getString("spring.datasource.username");
        password = resource.getString("spring.datasource.password");
        showSuccess("数据库配置读取：\n\tdriver=" + driver +"\n\turl=" + url + "\n\tusername=" + username + "\n\tpassword="+ password);
    }
    // 连接数据库
    private void openConnection() {
        try {
            Class.forName(driver);
            connection = DriverManager.getConnection(url, username, password);
            showSuccess("数据库连接成功：\n\tconnection=" + connection.getClass().getName());
        } catch (ClassNotFoundException | SQLException e) {
            showError("数据库连接失败，流程终止！", e, false);
        }
    }
    // 关闭数据库
    private void closeConnection() {
        try {
            connection.close();
            showSuccess("数据库关闭成功；");
        } catch (SQLException e) {
            showError("数据库关闭失败，流程终止！", e, false);
        }
    }


    public Generator setTemplateConfig(String templateDir) {
    	step++;
    	if (step != 6) {
        	showError("步骤错误，流程终止！","第六步请执行setTemplateConfig(String templateDir", false);
        	return this;
        }
        cfg = new Configuration(Configuration.VERSION_2_3_29);
		cfg.setDefaultEncoding("UTF-8");
		try {
			cfg.setDirectoryForTemplateLoading(new File(templateDir));
			showSuccess("模板引擎加载成功；");
		} catch (IOException e) {
            showError("模板引擎加载失败，流程终止！", e, false);
		}
        return this;
	}
    /**
     * 创建实体类
     * @param templateName 模板文件
     * @param deleteExist true=删除已经文件重新创建，false=跳过此环节
     * @return this for chain
     */
    public Generator createEntity(String templateName, boolean deleteExist) {
    	step++;
        if (!runAble) return this;
		try {
			File entity = new File(configure.getEntityFile());
			if (entity.exists()) {
				if (deleteExist) {
					entity.delete();
				} else {
                    showSuccess("实体类已经存在，deleteExist=false，跳过此步骤");
                    return this;
				}
			}
			Map<String, Object> root = rootMap();
			root.put("fieldList", fieldList);
			root.put("importSet", entityImportSet);
			Writer out = new FileWriter(entity);
			cfg.getTemplate(templateName).process(root, out);
			out.flush();
			out.close();
			showSuccess("实体类创建成功；");
		} catch (Exception e) {
		    showError("实体类创建失败，流程继续！", e, true);
		}
        return this;
	}
    /**
     * 创建Mapper映射类
     * @param templateName 模板文件
     * @param deleteExist true=删除已经文件重新创建，false=跳过此环节
     * @return this for chain
     */
    public Generator createMapper(String templateName, boolean deleteExist) {
        step++;
        if (!runAble) return this;
        try {
            //System.out.println(configure.getMapperFile());
            File mapper = new File(configure.getMapperFile());
            if (mapper.exists()) {
                if (deleteExist) {
                    mapper.delete();
                } else {
                    showSuccess("映射类已经存在，deleteExist=false，跳过此步骤");
                    return this;
                }
            }
            Map<String, Object> root = rootMap();
            root.put("fieldList", fieldList);
            root.put("fieldNameList", columnNameSqlList);
            root.put("importSet", mapperImportSet);
            Writer out = new FileWriter(mapper);
            cfg.getTemplate(templateName).process(root, out);
            out.flush();
            out.close();
            showSuccess("映射类创建成功；");
        } catch (Exception e) {
            showError("映射类创建失败，流程继续！", e, true);
        }
        return this;
    }

    /**
     * 创建Service服务类
     * @param templateName 模板文件
     * @param deleteExist true=删除已经文件重新创建，false=跳过此环节
     * @return this for chain
     */
    public Generator createService(String templateName, boolean deleteExist) {
        step++;
        if (!runAble) return this;
        try {
            //System.out.println(configure.getServiceFile());
            File service = new File(configure.getServiceFile());
            if (service.exists()) {
                if (deleteExist) {
                    service.delete();
                } else {
                    showSuccess("服务类已经存在，deleteExist=false，跳过此步骤");
                    return this;
                }
            }
            Map<String, Object> root = rootMap();
            root.put("id", id);
            root.put("fieldList", fieldList);
            root.put("importSet", mapperImportSet);
            Writer out = new FileWriter(service);
            cfg.getTemplate(templateName).process(root, out);
            out.flush();
            out.close();
            showSuccess("服务类创建成功；");
        } catch (Exception e) {
            showError("服务类创建失败，流程继续！", e, true);
        }
        return this;
    }
    private Map<String, Object> rootMap() {
    	Map<String, Object> map = new HashMap<>();
    	map.put("configure", configure);
    	map.put("id", id);
    	map.put("ASCLL_35", "#");
    	map.put("ASCLL_36", "$");
    	map.put("ASCLL_123", "{");
    	map.put("ASCLL_125", "}");
    	return map;
    }

    public void over() {
    	step++;
    	showSuccess("逆向工程执行结束，请刷新项目文件（包） ！！！");
	}
    protected final void showSuccess (String str) {
        System.out.println(String.format("第<%s>步 - - > %s", step, str));
    }
    protected final void showError (String str, Exception e, boolean continu) {
    	showError(str, e.getMessage(), continu);
    }
    protected final void showError (String str, String reason, boolean continu) {
        System.err.println(String.format("第<%s>步 - - > %s", step, str));
        System.err.println("- 错误原因 - - > " + reason);
        if(!continu) System.exit(0);
    }

    protected final boolean isEmpty(String str) {
        return str == null | "".equals(str.trim()) | "null".equalsIgnoreCase(str.trim());
    }

    protected abstract void init();
    // 将数据表转为实体类
    protected abstract void convertTable2Entity();
    // 将数据表字段转为查询列
    protected abstract void convertColumnNameSqlList();


    private static final Pattern LINE_PATTERN = Pattern.compile("_(\\w)");
    private static final Pattern HUMP_PATTERN = Pattern.compile("[A-Z]");
    /** 下划线转驼峰 */
    public static String lineToHump(String str) {
        str = str.toLowerCase();
        Matcher matcher = LINE_PATTERN.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }
    /** 驼峰转下划线 */
    public static String humpToLine(String str) {
        Matcher matcher = HUMP_PATTERN.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }
}
